/**
 * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
const HEX_COLOR_REGEXP = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
const RGB_COLOR_REGEXP = /^rgb\([ ]?([0-9]{1,3}[ %]?,[ ]?){2,3}[0-9]{1,3}[ %]?\)$/i;
const RGBA_COLOR_REGEXP = /^rgba\([ ]?([0-9]{1,3}[ %]?,[ ]?){3}(1|[0-9]+%|[0]?\.?[0-9]+)\)$/i;
const HSL_COLOR_REGEXP = /^hsl\([ ]?([0-9]{1,3}[ %]?[,]?[ ]*){3}(1|[0-9]+%|[0]?\.?[0-9]+)?\)$/i;
const HSLA_COLOR_REGEXP = /^hsla\([ ]?([0-9]{1,3}[ %]?,[ ]?){2,3}(1|[0-9]+%|[0]?\.?[0-9]+)\)$/i;
// Note: This regexp hardcodes a single level of nested () for values such as `calc( var( ...) + ...)`.
// If this gets more complex, a proper parser should be used instead.
const CSS_SHORTHAND_VALUE_REGEXP = /\w+\((?:[^()]|\([^()]*\))*\)|\S+/gi;
const COLOR_NAMES = new Set([
    // CSS Level 1
    'black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia',
    'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua',
    // CSS Level 2 (Revision 1)
    'orange',
    // CSS Color Module Level 3
    'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown',
    'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan',
    'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta',
    'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
    'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
    'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod',
    'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush',
    'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray',
    'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray',
    'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'magenta', 'mediumaquamarine',
    'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
    'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite',
    'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred',
    'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon',
    'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow',
    'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'whitesmoke', 'yellowgreen',
    // CSS Color Module Level 3 (System Colors)
    'activeborder', 'activecaption', 'appworkspace', 'background', 'buttonface', 'buttonhighlight', 'buttonshadow',
    'buttontext', 'captiontext', 'graytext', 'highlight', 'highlighttext', 'inactiveborder', 'inactivecaption',
    'inactivecaptiontext', 'infobackground', 'infotext', 'menu', 'menutext', 'scrollbar', 'threeddarkshadow',
    'threedface', 'threedhighlight', 'threedlightshadow', 'threedshadow', 'window', 'windowframe', 'windowtext',
    // CSS Color Module Level 4
    'rebeccapurple',
    // Keywords
    'currentcolor', 'transparent'
]);
/**
 * Checks if string contains [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color) CSS value.
 *
 * ```ts
 * isColor( '#f00' );						// true
 * isColor( '#AA00BB33' );					// true
 * isColor( 'rgb(0, 0, 250)' );				// true
 * isColor( 'hsla(240, 100%, 50%, .7)' );	// true
 * isColor( 'deepskyblue' );				// true
 * ```
 *
 * **Note**: It does not support CSS Level 4 whitespace syntax, system colors and radius values for HSL colors.
 */
export function isColor(string) {
    // As far as I was able to test checking some pre-conditions is faster than joining each test with ||.
    if (string.startsWith('#')) {
        return HEX_COLOR_REGEXP.test(string);
    }
    if (string.startsWith('rgb')) {
        return RGB_COLOR_REGEXP.test(string) || RGBA_COLOR_REGEXP.test(string);
    }
    if (string.startsWith('hsl')) {
        return HSL_COLOR_REGEXP.test(string) || HSLA_COLOR_REGEXP.test(string);
    }
    // Array check > RegExp test.
    return COLOR_NAMES.has(string.toLowerCase());
}
const lineStyleValues = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
/**
 * Checks if string contains [line style](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style) CSS value.
 */
export function isLineStyle(string) {
    return lineStyleValues.includes(string);
}
const lengthRegExp = /^([+-]?[0-9]*([.][0-9]+)?(px|cm|mm|in|pc|pt|ch|em|ex|rem|vh|vw|vmin|vmax)|0)$/;
/**
 * Checks if string contains [length](https://developer.mozilla.org/en-US/docs/Web/CSS/length) CSS value.
 */
export function isLength(string) {
    return lengthRegExp.test(string);
}
const PERCENTAGE_VALUE_REGEXP = /^[+-]?[0-9]*([.][0-9]+)?%$/;
/**
 * Checks if string contains [percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/percentage) CSS value.
 */
export function isPercentage(string) {
    return PERCENTAGE_VALUE_REGEXP.test(string);
}
const repeatValues = ['repeat-x', 'repeat-y', 'repeat', 'space', 'round', 'no-repeat'];
/**
 * Checks if string contains [background repeat](https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat) CSS value.
 */
export function isRepeat(string) {
    return repeatValues.includes(string);
}
const positionValues = ['center', 'top', 'bottom', 'left', 'right'];
/**
 * Checks if string contains [background position](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position) CSS value.
 */
export function isPosition(string) {
    return positionValues.includes(string);
}
const attachmentValues = ['fixed', 'scroll', 'local'];
/**
 * Checks if string contains [background attachment](https://developer.mozilla.org/en-US/docs/Web/CSS/background-attachment) CSS value.
 */
export function isAttachment(string) {
    return attachmentValues.includes(string);
}
const urlRegExp = /^url\(/;
/**
 * Checks if string contains [URL](https://developer.mozilla.org/en-US/docs/Web/CSS/url) CSS value.
 */
export function isURL(string) {
    return urlRegExp.test(string);
}
/**
 * Parses box sides as individual values.
 */
export function getBoxSidesValues(value = '') {
    if (value === '') {
        return { top: undefined, right: undefined, bottom: undefined, left: undefined };
    }
    const values = getShorthandValues(value);
    const top = values[0];
    const bottom = values[2] || top;
    const right = values[1] || top;
    const left = values[3] || right;
    return { top, bottom, right, left };
}
/**
 * Default reducer for CSS properties that concerns edges of a box
 * [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notations:
 *
 * ```ts
 * stylesProcessor.setReducer( 'padding', getBoxSidesValueReducer( 'padding' ) );
 * ```
 */
export function getBoxSidesValueReducer(styleShorthand) {
    return (value) => {
        const { top, right, bottom, left } = value;
        const reduced = [];
        if (![top, right, left, bottom].every(value => !!value)) {
            if (top) {
                reduced.push([styleShorthand + '-top', top]);
            }
            if (right) {
                reduced.push([styleShorthand + '-right', right]);
            }
            if (bottom) {
                reduced.push([styleShorthand + '-bottom', bottom]);
            }
            if (left) {
                reduced.push([styleShorthand + '-left', left]);
            }
        }
        else {
            reduced.push([styleShorthand, getBoxSidesShorthandValue(value)]);
        }
        return reduced;
    };
}
/**
 * Returns a [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notation
 * of a CSS property value.
 *
 * ```ts
 * getBoxSidesShorthandValue( { top: '1px', right: '1px', bottom: '2px', left: '1px' } );
 * // will return '1px 1px 2px'
 * ```
 */
export function getBoxSidesShorthandValue({ top, right, bottom, left }) {
    const out = [];
    if (left !== right) {
        out.push(top, right, bottom, left);
    }
    else if (bottom !== top) {
        out.push(top, right, bottom);
    }
    else if (right !== top) {
        out.push(top, right);
    }
    else {
        out.push(top);
    }
    return out.join(' ');
}
/**
 * Creates a normalizer for a [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) 1-to-4 value.
 *
 * ```ts
 * stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) );
 * ```
 */
export function getPositionShorthandNormalizer(shorthand) {
    return (value) => {
        return {
            path: shorthand,
            value: getBoxSidesValues(value)
        };
    };
}
/**
 * Parses parts of a 1-to-4 value notation - handles some CSS values with spaces (like RGB()).
 *
 * ```ts
 * getShorthandValues( 'red blue RGB(0, 0, 0)');
 * // will return [ 'red', 'blue', 'RGB(0, 0, 0)' ]
 * ```
 */
export function getShorthandValues(string) {
    const matches = string.matchAll(CSS_SHORTHAND_VALUE_REGEXP);
    return Array.from(matches).map(i => i[0]);
}
